<?php
/**
 * CommentController.php
 *
 * @category   Madoqua
 * @package    Module
 * @subpackage Blog
 */

/** abstract base controller */
require_once 'Madoqua/Controller/Action/Abstract.php';

/** action cacher */
require_once 'Madoqua/Cache/Action.php';

/** Zend service akismet */
require_once 'Zend/Service/Akismet.php';

/** OpenID consumer */
require_once 'Zend/OpenId/Consumer.php';

/** OpenID storage */
require_once 'Zend/OpenId/Consumer/Storage/File.php';

/** Sreg extension for OpenID */
require_once 'Zend/OpenId/Extension/Sreg.php';

/**
 * Blog_CommentController
 *
 * Comment controller
 *
 * @category   Madoqua
 * @package    Module
 * @subpackage Blog
 */
class Blog_CommentController extends Madoqua_Controller_Action_Abstract
{

    /**
     * Post model
     *
     * @var Blog_Post
     */
    private $_postModel;

    /**
     * Comments model
     *
     * @var Blog_Comment
     */
    private $_commentModel;

    /**
     * redirector helper
     *
     * @var Zend_Controller_Action_Helper_Redirector
     */
    private $_redirector;

    /**
     * initialization
     */
    public function init()
    {
        parent::init();

        $this->_modelLoader->load(array('Blog_Post', 'Blog_Comment'));
        $this->_postModel = new Blog_Post();
        $this->_commentModel = new Blog_Comment();
        //set up model

        $this->_redirector = $this->getHelper('redirector');
        //redirector helper
    }

    /**
     * list comments for a post
     */
    public function listAction()
    {
        $post = $this->getPost();

        $comments = $this->_commentModel->fetchByPost($post);
        //select all accepted comments for $post

        $this->view->post = $post;
        $this->view->comments = $comments;
    }

    /**
     * post a comment for a post
     */
    public function createAction()
    {
        $post = $this->getPost();
        //the matching post

        $form = $this->_formLoader->get();
        //the form
        $form->setRouteAction(array('slug' => $post->slug), 'post_read', true);
        //the action points to this action, not the post we're commenting to
        $this->getRequest()->setParam('form', $form);
        //remember in request
        $this->view->form = $form;
        //and set it in the view

        if ($form->isPrg() && $form->comment->isValid($form->getPrgData())) {
            //form has been submitted and at least the comment part is valid
            
            $allowOpenId = (bool) $this->_config->comments->allow_openid;
            //allow open id?
                        
            if ($allowOpenId && $form->getSubForm('openid')->isValid($form->getPrgData())) {
                //user submitted openid (and it's not empty)
                $this->openId();
                //let the openid action worry about it
            } else {
                //openid check

                $form->openid->reset();
                //if openid subform isn't valid, remove it's data

                if ($form->standard->isValid($form->getPrgData())) {
                    //name form is valid
                    $data = $form->getModelValues($this->_commentModel);
                    $this->save($data);
                    //save the comment
                }
            }
        }
    }

    /**
     * open id check and retrieve user data
     */
    private function openId()
    {
        $dir = Madoqua_Application::getDataDirectory() . DIRECTORY_SEPARATOR  . 'openid/';
        $storage = new Zend_OpenId_Consumer_Storage_File($dir);
        //OpenID storage

        $consumer = new Zend_OpenId_Consumer($storage, true);
        //OpenID consumer

        $sreg = new Zend_OpenId_Extension_Sreg(array(
            'nickname' => true,
            'email' => true));
        //Sreg extension

        $form = $this->getRequest()->getParam('form');
        //the form

        if (!($form instanceof Madoqua_Form)) {
            throw new Exception('Form not set or invalid');
        }
        //retrieve the form

        $post = $this->getPost();
        //the post we're dealing with

        if ($mode = $this->getRequest()->getQuery('openid_mode', false)) {
            //we have a succesful return \ó/

            $identifier = null;
            //will be filled by the consumer
            if ($consumer->verify($this->getRequest()->getQuery(), $identifier, $sreg)) {
                //it verified (woo, valid!)

                $sregData = $sreg->getProperties();
                //sreg data
                $data = $form->getModelValues($this->_commentModel);
                //the original session data
                $data['name'] = $sregData['nickname'];
                $data['email'] = $sregData['email'];
                //overwrite/set srg data
                $this->save($data);
                //save it

            } else {
                //FIXME uh ohw!
                $form->openid->getElement('identifier')->setErrors(array('Could not get a valid openid response'));
            }

            return;
        }

        $identifier = $form->openid->getValue('identifier');
        //the openid identifier

        $url = $this->_config->url . $this->getHelper('urlRoute')->get(array('slug' => $post->slug), 'post_read');
        //url to *this* action, not the form

        if (!$consumer->login($identifier, $url, $this->_config->url, $sreg)) {
            //openID login failed
            //FIXME do something useful
            $form->openid->getElement('identifier')->setErrors(array('Could not get a valid openid response'));
        }
    }

    /**
     * save a new comment
     *
     * @param array $data
     */
    private function save($data)
    {
        $post = $this->getPost();
        //post
        $comment = $this->_commentModel->fetchNew();
        //create a new comment
        $comment->setFromArray($data);
        //set the form variables
        $comment->post_id = $post->id;
        //set the post id to the current post
        $comment->accepted = $this->_config->comments->autoaccept;
        //auto accept this comment or not?
        $comment->spam = 0;
        //default... isn't spam (although it probably is, damn intarwebz)
        $comment->posted = time();
        //the time of the comment
        $comment->user_agent = $_SERVER['HTTP_USER_AGENT'];
        $comment->ip = $_SERVER['REMOTE_ADDR'];
        //ip and user agent of the request

        $result = $comment->save();
        //save new comment
        Madoqua_Cache_Action::clean(array('controller_comment'));
        //clean comment cache

        if ($this->_config->comments->useakismet) {
            //the comment says we should use akismet... so let's do that :)
            $this->getRequest()->setParam('comment', $comment);
            //remember comment in the request

            $this->_forward('akismet', 'comment', 'blog');
            //forward to the akismet checking action
        } else {
            $this->_redirector->gotoRoute(array('slug' => $post->slug), 'post_read', true);
            //redirect back to post/read
        }
    }

    /**
     * akismet checking of comments
     *
     * @todo does this really have to be an action?
     */
    public function akismetAction()
    {
        $this->getHelper('viewRenderer')->setNoRender();
        //no rendering for this action

        $comment = $this->getRequest()->getParam('comment');
        if (!($comment instanceof Blog_Post_Row)) {
            throw new Exception('Comment param invalid');
        }
        //the comment

        $apiKey = $this->_config->comments->akismetkey;
        $blogUrl = $this->_config->url;
        $akismet = new Zend_Service_Akismet($apiKey, $blogUrl);
        //akismet service

        $data = array(
            'user_ip'              => $comment->ip,
            'user_agent'           => $comment->user_agent,
            'comment_type'         => 'comment',
            'comment_author'       => $comment->name,
            'comment_author_email' => $comment->email,
            'comment_content'      => $comment->content
        );

        try {
            $comment->spam = (int) $akismet->isSpam($data);
            //is this comment spam?
        } catch (Exception $e) {
            $comment->spam = 0;
        }
        //try to figure out whether this is spam or not

        if (!$comment->spam)
        {
            $comment->accepted = 1;
        }
        //accept the comment if it isn't spam

        $comment->save();
        //save the comment... again

        $this->_redirector->gotoRouteAndExit(array('slug' => $this->getPost()->slug), 'post_read');
        //redirect back
    }


    /**
     * get matching post,
     *
     * will search request for either a row in 'post', or a slug to find the post with
     *
     * @return Zend_Db_Table_Row
     */
    private function getPost()
    {
        $post = $this->_getParam('post', false);
        if (!$post)
        {
            $slug = $this->_getParam('slug');
            $post = $this->_postModel->fetchBySlug($slug);
        }
        //retrieve/load post

        if (!($post instanceof Blog_Post_Row))
        {
            throw new Exception('Post param not valid');
            //FIXME better exception
        }

        return $post;

    }
}